/*
    SUSAN® - Sole of Unix Save ANything

   
   Copyright (C) 2016-2020 Hechzh Property

   
*/
/*
 * Kern Sibbald, May MM
 */
/**
 * @file
 * BAREOS Sock Class definition
 * Note, the old non-class code is in bnet.c, and the
 * new class code associated with this file is in bsock.c
 *
 * Zero message_length from other end indicates soft eof (usually
 * end of some binary data stream, but not end of conversation).
 *
 * Negative message_length, is special "signal" (no data follows).
 * See below for SIGNAL codes.
 */

#ifndef BAREOS_LIB_BSOCK_H_
#define BAREOS_LIB_BSOCK_H_

#include <include/bareos.h>
#include <mutex>
#include <functional>
#include <cassert>
#include "lib/address_conf.h"
#include "lib/bnet_network_dump.h"
#include "lib/tls.h"
#include "lib/s_password.h"
#include "lib/tls_conf.h"
#include "include/version_numbers.h"

struct btimer_t; /* forward reference */
class BareosSocket;
class Tls;
class BStringList;
class QualifiedResourceNameTypeConverter;
class dlist;
btimer_t* StartBsockTimer(BareosSocket* bs, uint32_t wait);
void StopBsockTimer(btimer_t* wid);

class BareosSocket {
  /*
   * Note, keep this public part before the private otherwise
   *  bat breaks on some systems such as RedHat.
   */
 public:
  int fd_;                     /* Socket file descriptor */
  uint64_t read_seqno;         /* Read sequence number */
  POOLMEM* msg;                /* Message pool buffer */
  POOLMEM* errmsg;             /* Edited error message */
  int spool_fd_;               /* Spooling file */
  IPADDR* src_addr;            /* IP address to source connections from */
  uint32_t in_msg_no;          /* Input message number */
  uint32_t out_msg_no;         /* Output message number */
  int32_t message_length;      /* Message length */
  volatile time_t timer_start; /* Time started read/write */
  int b_errno;                 /* BareosSocket errno */
  int blocking_;       /* Blocking state (0 = nonblocking, 1 = blocking) */
  volatile int errors; /* Incremented for each error on socket */
  volatile bool suppress_error_msgs_; /* Set to suppress error messages */
  int sleep_time_after_authentication_error;

  struct sockaddr client_addr;  /* Client's IP address */
  struct sockaddr_in peer_addr; /* Peer's IP address */
  void SetTlsEstablished() { tls_established_ = true; }
  bool TlsEstablished() const { return tls_established_; }
  std::shared_ptr<Tls> tls_conn;      /* Associated tls connection */
  std::unique_ptr<Tls> tls_conn_init; /* during initialization */
  BareosVersionNumber connected_daemon_version_;

 protected:
  JobControlRecord* jcr_; /* JobControlRecord or NULL for error msgs */
  std::shared_ptr<std::mutex> mutex_;
  char* who_;            /* Name of daemon to which we are talking */
  char* host_;           /* Host name/IP */
  int port_;             /* Desired port */
  btimer_t* tid_;        /* Timer id */
  boffset_t data_end_;   /* Offset of last valid data written */
  int32_t FileIndex_;    /* Last valid attr spool FI */
  bool timed_out_;       /* Timed out in read/write */
  bool terminated_;      /* Set when BNET_TERMINATE arrives */
  bool cloned_;          /* Set if cloned BareosSocket */
  bool spool_;           /* Set for spooling */
  bool use_bursting_;    /* Set to use bandwidth bursting */
  bool use_keepalive_;   /* Set to use keepalive on the socket */
  int64_t bwlimit_;      /* Set to limit bandwidth */
  int64_t nb_bytes_;     /* Bytes sent/recv since the last tick */
  btime_t last_tick_;    /* Last tick used by bwlimit */
  bool tls_established_; /* is true when tls connection is established */
  std::unique_ptr<BnetDump> bnet_dump_;

  virtual void FinInit(JobControlRecord* jcr,
                       int sockfd,
                       const char* who,
                       const char* host,
                       int port,
                       struct sockaddr* lclient_addr) = 0;
  virtual bool open(JobControlRecord* jcr,
                    const char* name,
                    const char* host,
                    char* service,
                    int port,
                    utime_t heart_beat,
                    int* fatal) = 0;

 private:
  bool TwoWayAuthenticate(JobControlRecord* jcr,
                          const std::string own_qualified_name,
                          const char* identity,
                          s_password& password,
                          TlsResource* tls_resource,
                          bool initiated_by_remote);
  bool DoTlsHandshakeWithClient(TlsConfigCert* local_tls_cert,
                                JobControlRecord* jcr);
  bool DoTlsHandshakeWithServer(TlsConfigCert* local_tls_cert,
                                const char* identity,
                                const char* password,
                                JobControlRecord* jcr);
  void ParameterizeTlsCert(Tls* tls_conn, TlsResource* tls_resource);
  void SetBnetDump(std::unique_ptr<BnetDump>&& bnet_dump)
  {
    // do not set twice
    assert(!bnet_dump_);
    bnet_dump_ = std::move(bnet_dump);
  }

 public:
  BareosSocket();
  BareosSocket(const BareosSocket& other);
  virtual ~BareosSocket();

  /* Methods -- in bsock.c */
  //  void free_bsock();
  void CloseTlsConnectionAndFreeMemory();
  virtual BareosSocket* clone() = 0;
  virtual bool connect(JobControlRecord* jcr,
                       int retry_interval,
                       utime_t max_retry_time,
                       utime_t heart_beat,
                       const char* name,
                       const char* host,
                       char* service,
                       int port,
                       bool verbose) = 0;
  virtual int32_t recv() = 0;
  virtual bool send() = 0;
  virtual int32_t read_nbytes(char* ptr, int32_t nbytes) = 0;
  virtual int32_t write_nbytes(char* ptr, int32_t nbytes) = 0;
  virtual void close() = 0;   /* close connection and destroy packet */
  virtual void destroy() = 0; /* destroy socket packet */
  virtual int GetPeer(char* buf, socklen_t buflen) = 0;
  virtual bool SetBufferSize(uint32_t size, int rw) = 0;
  virtual int SetNonblocking() = 0;
  virtual int SetBlocking() = 0;
  virtual void RestoreBlocking(int flags) = 0;
  virtual bool ConnectionReceivedTerminateSignal() = 0;
  /*
   * Returns: 1 if data available, 0 if timeout, -1 if error
   */
  virtual int WaitData(int sec, int usec = 0) = 0;
  virtual int WaitDataIntr(int sec, int usec = 0) = 0;
  bool fsend(const char*, ...);
  bool vfsend(const char* fmt, va_list ap);
  bool send(const char* msg_in, uint32_t nbytes);
  void SetKillable(bool killable);
  bool signal(int signal);
  const char* bstrerror(); /* last error on socket */
  bool despool(void UpdateAttrSpoolSize(ssize_t size), ssize_t tsize);
  bool ConsoleAuthenticateWithDirector(JobControlRecord* jcr,
                                       const char* name,
                                       s_password& password,
                                       TlsResource* tls_resource,
                                       const std::string& own_qualified_name,
                                       BStringList& response_args,
                                       uint32_t& response_id);
  bool ParameterizeAndInitTlsConnection(TlsResource* tls_resource,
                                        const char* identity,
                                        const char* password,
                                        bool initiated_by_remote);
  bool ParameterizeAndInitTlsConnectionAsAServer(ConfigurationParser* config);
  bool DoTlsHandshake(TlsPolicy remote_tls_policy,
                      TlsResource* tls_resource,
                      bool initiated_by_remote,
                      const char* identity,
                      const char* password,
                      JobControlRecord* jcr);
  bool DoTlsHandshakeAsAServer(ConfigurationParser* config,
                               JobControlRecord* jcr = nullptr);
  bool SetLocking();   /* in bsock.c */
  void ClearLocking(); /* in bsock.c */
  void SetSourceAddress(dlist* src_addr_list);
  void ControlBwlimit(int bytes); /* in bsock.c */
  bool EvaluateCleartextBareosHello(bool& cleartext,
                                    std::string& client_name_out,
                                    std::string& r_code_str_out,
                                    BareosVersionNumber& version_out) const;
  void OutputCipherMessageString(std::function<void(const char*)>);
  void GetCipherMessageString(std::string& str) const;
  bool ReceiveAndEvaluateResponseMessage(uint32_t& id_out,
                                         BStringList& args_out);
  bool FormatAndSendResponseMessage(uint32_t id,
                                    const BStringList& list_of_agruments);
  bool FormatAndSendResponseMessage(uint32_t id, const std::string& str);

  bool AuthenticateOutboundConnection(JobControlRecord* jcr,
                                      const std::string own_qualified_name,
                                      const char* identity,
                                      s_password& password,
                                      TlsResource* tls_resource);

  bool AuthenticateInboundConnection(JobControlRecord* jcr,
                                     ConfigurationParser* my_config,
                                     const char* name,
                                     s_password& password,
                                     TlsResource* tls_resource);

  void SetJcr(JobControlRecord* jcr) { jcr_ = jcr; }
  void SetWho(char* who) { who_ = who; }
  void SetHost(char* host) { host_ = host; }
  void SetPort(int port) { port_ = port; }
  char* who() { return who_; }
  char* host() { return host_; }
  int port() { return port_; }
  JobControlRecord* jcr() { return jcr_; }
  JobControlRecord* get_jcr() { return jcr_; }
  bool IsSpooling() { return spool_; }
  bool IsTerminated() { return terminated_; }
  bool IsTimedOut() { return timed_out_; }
  bool IsStop() { return errors || IsTerminated(); }
  bool IsError()
  {
    errno = b_errno;
    return errors;
  }
  void SetDataEnd(int32_t FileIndex)
  {
    if (spool_ && FileIndex > FileIndex_) {
      FileIndex_ = FileIndex - 1;
      data_end_ = lseek(spool_fd_, 0, SEEK_CUR);
    }
  }
  boffset_t get_data_end() { return data_end_; }
  int32_t get_FileIndex() { return FileIndex_; }
  void SetBwlimit(int64_t maxspeed) { bwlimit_ = maxspeed; }
  bool UseBwlimit() { return bwlimit_ > 0; }
  void SetBwlimitBursting() { use_bursting_ = true; }
  void clear_bwlimit_bursting() { use_bursting_ = false; }
  void SetSpooling() { spool_ = true; }
  void ClearSpooling() { spool_ = false; }
  void SetTimedOut() { timed_out_ = true; }
  void ClearTimedOut() { timed_out_ = false; }
  void SetTerminated() { terminated_ = true; }
  void StartTimer(int sec) { tid_ = StartBsockTimer(this, sec); }
  void StopTimer() { StopBsockTimer(tid_); }
  void LockMutex();
  void UnlockMutex();
  void InitBnetDump(std::string own_qualified_name);
  void SetBnetDumpDestinationQualifiedName(
      std::string destination_qualified_name);
  bool IsBnetDumpEnabled() const { return bnet_dump_.get() != nullptr; }
};

/**
 *  Signal definitions for use in BnetSig()
 *  Note! These must be negative.  There are signals that are generated
 *   by the bsock software not by the OS ...
 */
enum
{
  BNET_EOD = -1,           /* End of data stream, new data may follow */
  BNET_EOD_POLL = -2,      /* End of data and poll all in one */
  BNET_STATUS = -3,        /* Send full status */
  BNET_TERMINATE = -4,     /* Conversation terminated, doing close() */
  BNET_POLL = -5,          /* Poll request, I'm hanging on a read */
  BNET_HEARTBEAT = -6,     /* Heartbeat Response requested */
  BNET_HB_RESPONSE = -7,   /* Only response permited to HB */
  BNET_xxxxxxPROMPT = -8,  /* No longer used -- Prompt for subcommand */
  BNET_BTIME = -9,         /* Send UTC btime */
  BNET_BREAK = -10,        /* Stop current command -- ctl-c */
  BNET_START_SELECT = -11, /* Start of a selection list */
  BNET_END_SELECT = -12,   /* End of a select list */
  BNET_INVALID_CMD = -13,  /* Invalid command sent */
  BNET_CMD_FAILED = -14,   /* Command failed */
  BNET_CMD_OK = -15,       /* Command succeeded */
  BNET_CMD_BEGIN = -16,    /* Start command execution */
  BNET_MSGS_PENDING = -17, /* Messages pending */
  BNET_MAIN_PROMPT = -18,  /* Server ready and waiting */
  BNET_SELECT_INPUT = -19, /* Return selection input */
  BNET_WARNING_MSG = -20,  /* Warning message */
  BNET_ERROR_MSG = -21,    /* Error message -- command failed */
  BNET_INFO_MSG = -22,     /* Info message -- status line */
  BNET_RUN_CMD = -23,      /* Run command follows */
  BNET_YESNO = -24,        /* Request yes no response */
  BNET_START_RTREE = -25,  /* Start restore tree mode */
  BNET_END_RTREE = -26,    /* End restore tree mode */
  BNET_SUB_PROMPT = -27,   /* Indicate we are at a subprompt */
  BNET_TEXT_INPUT = -28    /* Get text input from user */
};

#define BNET_SETBUF_READ 1  /* Arg for BnetSetBufferSize */
#define BNET_SETBUF_WRITE 2 /* Arg for BnetSetBufferSize */

/**
 * Return status from BnetRecv()
 * Note, the HARDEOF and ERROR refer to comm status/problems
 *  rather than the BNET_xxx above, which are software signals.
 */
enum
{
  BNET_SIGNAL = -1,
  BNET_HARDEOF = -2,
  BNET_ERROR = -3
};

#endif /* BAREOS_LIB_BSOCK_H_ */
